// Package config config/config.go
package config

import (
	"fmt"
	"os"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper" // 导入 Viper
	"px-table-checksum-go/models"
)

// DBConfig 数据库连接配置
type DBConfig struct {
	Host            string `yaml:"host" mapstructure:"host"`                           // 主机地址
	Port            int    `yaml:"port" mapstructure:"port"`                           // 端口
	User            string `yaml:"user" mapstructure:"user"`                           // 用户名
	Password        string `yaml:"password" mapstructure:"password"`                   // 密码
	Database        string `yaml:"database,omitempty" mapstructure:"database"`         // 数据库名 (可选)
	Charset         string `yaml:"charset" mapstructure:"charset"`                     // 字符集
	Params          string `yaml:"params,omitempty" mapstructure:"params"`             // 额外连接参数
	MaxOpenConns    int    `yaml:"max_open_conns" mapstructure:"max_open_conns"`       // 最大打开连接数
	MaxIdleConns    int    `yaml:"max_idle_conns" mapstructure:"max_idle_conns"`       // 最大空闲连接数
	ConnMaxLifetime int    `yaml:"conn_max_lifetime" mapstructure:"conn_max_lifetime"` // 连接最大生命周期 (秒)
}

// DSN 生成数据库连接 DSN 字符串
func (c *DBConfig) DSN(dbName ...string) string {
	var finalDBName string
	if len(dbName) > 0 {
		finalDBName = dbName[0] // 如果提供了dbName参数，则使用它
	} else {
		finalDBName = c.Database // 否则使用配置中的数据库名
	}
	// 格式化DSN字符串
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", c.User, c.Password, c.Host, c.Port, finalDBName, c.Charset)
	// 添加额外参数
	if c.Params != "" {
		if !strings.Contains(dsn, "?") {
			dsn += "?"
		} else {
			dsn += "&"
		}
		dsn += c.Params
	}
	return dsn
}

// ID 生成数据库标识符 host:port
func (c *DBConfig) ID() string {
	return fmt.Sprintf("%s:%d", c.Host, c.Port)
}

// RedisConfig Redis 配置
type RedisConfig struct {
	Host              string `yaml:"host,omitempty" mapstructure:"host"`                     // 主机 (如果AddressStr未设置)
	Port              int    `yaml:"port,omitempty" mapstructure:"port"`                     // 端口 (如果AddressStr未设置)
	AddressStr        string `yaml:"address" mapstructure:"address"`                         // Redis地址 (例如 "127.0.0.1:6379", 优先于Host/Port)
	Password          string `yaml:"password" mapstructure:"password"`                       // 密码
	DB                int    `yaml:"db" mapstructure:"db"`                                   // Redis数据库编号
	QueueNamePrefix   string `yaml:"queue_name_prefix" mapstructure:"queue_name_prefix"`     // 队列名称前缀
	SourcePoolSize    int    `yaml:"source_pool_size" mapstructure:"source_pool_size"`       // 源端Redis连接池大小
	TargetWorkerCount int    `yaml:"target_worker_count" mapstructure:"target_worker_count"` // 目标端工作线程数
}

// Address 返回Redis的地址 host:port
func (c *RedisConfig) Address() string {
	if c.AddressStr != "" {
		return c.AddressStr // 优先使用AddressStr
	}
	return fmt.Sprintf("%s:%d", c.Host, c.Port) // 否则组合Host和Port
}

// Options 运行参数
type Options struct {
	ChunkSize         int    `yaml:"chunk_size" mapstructure:"chunk_size"`                 // 每个数据块的大小
	CalcCRC32InDB     bool   `yaml:"calc_crc32_in_db" mapstructure:"calc_crc32_in_db"`     // 是否在数据库端计算CRC32
	Mode              int    `yaml:"mode" mapstructure:"mode"`                             // 运行模式 (0:计算, 1:计算和比较, 2:仅比较)
	GenerateFixSQL    bool   `yaml:"generate_fix_sql" mapstructure:"generate_fix_sql"`     // 是否生成修复SQL文件
	RunFixSQL         bool   `yaml:"run_fix_sql" mapstructure:"run_fix_sql"`               // 是否自动执行修复SQL (危险!)
	SourceDBID        string `yaml:"source_db_id" mapstructure:"source_db_id"`             // 源数据库标识符
	TargetDBID        string `yaml:"target_db_id" mapstructure:"target_db_id"`             // 目标数据库标识符
	LogLevel          string `yaml:"log_level" mapstructure:"log_level"`                   // 日志级别 (debug, info, warn, error)
	QueueName         string `yaml:"-"`                                                    // 运行时生成，不从配置加载
	TaskTimeout       int    `yaml:"task_timeout" mapstructure:"task_timeout"`             // 任务超时时间 (秒)
	ResultExpire      int    `yaml:"result_expire" mapstructure:"result_expire"`           // 校验结果过期时间 (秒)
	Debug             bool   `yaml:"debug" mapstructure:"debug"`                           // 是否启用调试模式
	MaxErrors         int    `yaml:"max_errors" mapstructure:"max_errors"`                 // 最大允许错误数
	SourceConcurrency int    `yaml:"source_concurrency" mapstructure:"source_concurrency"` // 源端并发处理表的数量
	SchemaPattern     string `yaml:"schema_pattern" mapstructure:"schema_pattern"`         // 数据库模式匹配模式 (如果tables未指定)
	TablePattern      string `yaml:"table_pattern" mapstructure:"table_pattern"`           // 表名匹配模式 (如果tables未指定)
	MaxDetailRows     int    `yaml:"max_detail_rows" mapstructure:"max_detail_rows"`       // 每种差异最多显示的详细行数
}

// AppConfig 应用总配置
type AppConfig struct {
	Redis       RedisConfig         `yaml:"redis" mapstructure:"redis"`               // Redis配置
	DBChecksum  DBConfig            `yaml:"db_checksum" mapstructure:"db_checksum"`   // 校验结果数据库配置
	DBSource    DBConfig            `yaml:"db_source" mapstructure:"db_source"`       // 源数据库配置
	DBTarget    DBConfig            `yaml:"db_target" mapstructure:"db_target"`       // 目标数据库配置
	TablesCheck map[string][]string `yaml:"tables_check" mapstructure:"tables_check"` // 要检查的表 (map格式)
	Tables      []models.TableInfo  `yaml:"tables" mapstructure:"tables"`             // 要检查的表 (列表格式，优先于tables_check)
	Options     Options             `yaml:"options" mapstructure:"options"`           // 运行选项
}

// GlobalConfig 全局配置实例
var GlobalConfig AppConfig

// LoadConfig 加载配置文件并应用环境变量和默认值
func LoadConfig(configPath string) error {
	// 1. 设置 Viper 实例
	v := viper.New()
	v.SetConfigFile(configPath) // 指定配置文件路径
	v.SetConfigType("yaml")     // 指定配置文件类型

	// 2. 设置默认值
	setViperDefaults(v)

	// 3. 启用环境变量覆盖
	v.SetEnvPrefix("PX")                               // 设置环境变量前缀，例如 PX_DBSOURCE_HOST
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) // 将配置中的 . 替换为 _，例如 dbsource.host -> DBSOURCE_HOST
	v.AutomaticEnv()                                   // 自动读取环境变量

	// 4. 读取配置文件
	if err := v.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			log.Warnf("配置文件 %s 未找到，将仅使用默认值和环境变量", configPath)
		} else {
			return fmt.Errorf("读取配置文件 %s 失败: %w", configPath, err)
		}
	}

	// 5. 将配置反序列化到 GlobalConfig 结构体
	if err := v.Unmarshal(&GlobalConfig); err != nil {
		return fmt.Errorf("解析配置到结构体失败: %w", err)
	}

	// 6. 运行时生成 QueueName (确保每次运行唯一)
	if GlobalConfig.Options.QueueName == "" {
		GlobalConfig.Options.QueueName = fmt.Sprintf("%s_%s_%d",
			GlobalConfig.Redis.QueueNamePrefix,
			time.Now().Format("20060102150405"),
			os.Getpid(), // 加入进程ID增加唯一性
		)
		log.Debugf("运行时生成的 QueueName: %s", GlobalConfig.Options.QueueName)
	}

	// 7. 验证配置 (可选，但推荐)
	validation := ValidateConfig()
	if !validation.Valid {
		return fmt.Errorf("配置无效: %s", strings.Join(validation.Errors, ", "))
	}
	for _, warning := range validation.Warnings {
		log.Warnf("配置警告: %s", warning)
	}

	// 8. 设置日志级别 (如果在main中调用，这部分可以移到main)
	// initLogrus(GlobalConfig.Options.LogLevel) // 假设有 initLogrus 函数

	log.Infof("配置已加载: 源 %s, 目标 %s, 校验 %s",
		GlobalConfig.DBSource.ID(),
		GlobalConfig.DBTarget.ID(),
		GlobalConfig.DBChecksum.ID())

	return nil
}

// setViperDefaults 使用 Viper 设置默认值
func setViperDefaults(v *viper.Viper) {
	// DB 默认值
	v.SetDefault("dbsource.charset", "utf8mb4")
	v.SetDefault("dbsource.max_open_conns", 10)
	v.SetDefault("dbsource.max_idle_conns", 5)
	v.SetDefault("dbsource.conn_max_lifetime", 300) // 5分钟

	v.SetDefault("dbtarget.charset", "utf8mb4")
	v.SetDefault("dbtarget.max_open_conns", 10)
	v.SetDefault("dbtarget.max_idle_conns", 5)
	v.SetDefault("dbtarget.conn_max_lifetime", 300)

	v.SetDefault("dbchecksum.charset", "utf8mb4")
	v.SetDefault("dbchecksum.max_open_conns", 10)
	v.SetDefault("dbchecksum.max_idle_conns", 5)
	v.SetDefault("dbchecksum.conn_max_lifetime", 300)

	// Redis 默认值
	v.SetDefault("redis.source_pool_size", 10)
	v.SetDefault("redis.target_worker_count", 5)
	v.SetDefault("redis.queue_name_prefix", "px_checksum")

	// Options 默认值
	v.SetDefault("options.chunk_size", 1000)
	v.SetDefault("options.calc_crc32_in_db", true)
	v.SetDefault("options.mode", 1) // 默认计算并比较
	v.SetDefault("options.generate_fix_sql", true)
	v.SetDefault("options.run_fix_sql", false)
	v.SetDefault("options.log_level", "info")
	v.SetDefault("options.task_timeout", 3600)   // 1小时
	v.SetDefault("options.result_expire", 86400) // 24小时
	v.SetDefault("options.debug", false)
	v.SetDefault("options.max_errors", 10)
	v.SetDefault("options.source_concurrency", 2)
	v.SetDefault("options.max_detail_rows", 100)
}

// GetTablesToCheckCount 获取总共要检查的表的数量
func GetTablesToCheckCount() int {
	count := 0
	if len(GlobalConfig.Tables) > 0 {
		// 如果 Tables 列表不为空，优先使用它
		count = len(GlobalConfig.Tables)
	} else if len(GlobalConfig.TablesCheck) > 0 {
		// 否则，使用 TablesCheck map
		for _, tables := range GlobalConfig.TablesCheck {
			count += len(tables)
		}
	}
	// 注意：这里没有处理 SchemaPattern/TablePattern 的情况，
	// 因为那需要在连接数据库后动态获取表列表。
	return count
}

// ValidateConfig 验证配置的有效性 (基本保持不变，可根据需要调整)
func ValidateConfig() models.Validation {
	validation := models.Validation{
		Valid:    true,
		Errors:   []string{},
		Warnings: []string{},
	}

	// 验证数据库配置
	validateDBConfig("源数据库", GlobalConfig.DBSource, &validation, false)
	validateDBConfig("目标数据库", GlobalConfig.DBTarget, &validation, false)
	validateDBConfig("校验数据库", GlobalConfig.DBChecksum, &validation, true) // 校验库需要 database name

	// 验证Redis配置
	if GlobalConfig.Redis.Address() == ":" { // 检查地址是否有效
		validation.Errors = append(validation.Errors, "缺少 Redis 地址 (host/port 或 address)")
		validation.Valid = false
	}

	// 验证运行选项
	if GlobalConfig.Options.Mode < 0 || GlobalConfig.Options.Mode > 2 {
		validation.Errors = append(validation.Errors, "无效的运行模式，应为0、1或2")
		validation.Valid = false
	}
	if GlobalConfig.Options.ChunkSize <= 0 {
		validation.Errors = append(validation.Errors, "ChunkSize 必须大于 0")
		validation.Valid = false
	}
	if GlobalConfig.Options.TaskTimeout <= 0 {
		validation.Warnings = append(validation.Warnings, "任务超时时间未设置或无效，将使用默认值")
	}
	if GlobalConfig.Options.ResultExpire <= 0 {
		validation.Warnings = append(validation.Warnings, "结果过期时间未设置或无效，将使用默认值")
	}

	// 验证表配置 (根据新的逻辑调整)
	hasTablesConfig := len(GlobalConfig.Tables) > 0
	hasTablesCheckConfig := len(GlobalConfig.TablesCheck) > 0
	hasPatternConfig := GlobalConfig.Options.SchemaPattern != ""

	if !hasTablesConfig && !hasTablesCheckConfig && !hasPatternConfig {
		validation.Warnings = append(validation.Warnings, "未通过 tables, tables_check 或 schema_pattern 指定要检查的表")
	} else if hasTablesCheckConfig {
		isEmpty := true
		for schema, tables := range GlobalConfig.TablesCheck {
			if len(tables) == 0 {
				validation.Warnings = append(validation.Warnings, fmt.Sprintf("tables_check 中 Schema '%s' 没有指定要检查的表", schema))
			} else {
				isEmpty = false
			}
		}
		if isEmpty {
			validation.Warnings = append(validation.Warnings, "tables_check 配置中未指定任何表")
		}
	}

	// 验证DBID (Viper会自动处理，这里可以作为附加检查或移除)
	// 注意：ID() 会基于当前的 Host/Port 生成，可能在环境变量覆盖后变化
	if GlobalConfig.Options.SourceDBID == "" {
		validation.Warnings = append(validation.Warnings, "未设置源数据库ID (source_db_id)，将使用自动生成的ID")
		GlobalConfig.Options.SourceDBID = GlobalConfig.DBSource.ID() // 运行时设置
	}
	if GlobalConfig.Options.TargetDBID == "" {
		validation.Warnings = append(validation.Warnings, "未设置目标数据库ID (target_db_id)，将使用自动生成的ID")
		GlobalConfig.Options.TargetDBID = GlobalConfig.DBTarget.ID() // 运行时设置
	}

	return validation
}

// validateDBConfig 辅助函数验证单个数据库配置
func validateDBConfig(dbName string, cfg DBConfig, v *models.Validation, requireDB bool) {
	if cfg.Host == "" {
		v.Errors = append(v.Errors, fmt.Sprintf("缺少%s主机地址", dbName))
		v.Valid = false
	}
	if cfg.Port <= 0 {
		v.Errors = append(v.Errors, fmt.Sprintf("%s端口无效", dbName))
		v.Valid = false
	}
	if cfg.User == "" {
		v.Errors = append(v.Errors, fmt.Sprintf("缺少%s用户名", dbName))
		v.Valid = false
	}
	if requireDB && cfg.Database == "" {
		v.Errors = append(v.Errors, fmt.Sprintf("缺少%s数据库名", dbName))
		v.Valid = false
	}
	if cfg.Charset == "" {
		v.Errors = append(v.Errors, fmt.Sprintf("缺少%s字符集", dbName))
		v.Valid = false
	}
}
